package hujz.java.file_server._default;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class SessionFactoryUtils {

    private static Logger logger = LoggerFactory.getLogger(SessionFactoryUtils.class);

    /**
     * 按照配置生成 @{@link org.apache.ibatis.session.SqlSessionFactory}
     *
     * @param config    mybatis配置文件
     * @param mapperDir 扫描的sql-mapper的目录，必须在classpath下，不扫描jar中的配置
     * @return
     */
    public static org.apache.ibatis.session.SqlSessionFactory create(String config, String mapperDir) {
        return create(config, mapperDir, null);
    }

    public static org.apache.ibatis.session.SqlSessionFactory create(String config, String mapperDir, String plugBase){
        try {
            String resource = config;
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null, System.getProperties());
            Configuration configuration = parser.parse();

            Enumeration<URL> urlEnumeration = SessionFactoryUtils.class.getClassLoader().getResources(mapperDir);
            while (urlEnumeration.hasMoreElements()) {
                URL url = urlEnumeration.nextElement();
                logger.info("Will load \"{}\".", url.toURI());

                if ("file".equals(url.getProtocol())) {
                    File file = new File(url.toURI());
                    File[] xmls = file.listFiles(file1 -> file1.getName().toLowerCase().endsWith(".xml"));
                    for (int i = 0; i < xmls.length; i++) {
                        File xml = xmls[i];
                        logger.info("Load sql-mapper \"{}\".", xml.getAbsoluteFile());
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(new FileInputStream(xml), configuration, xml.getAbsolutePath(), configuration.getSqlFragments());
                        mapperParser.parse();
                    }
                } else {
                    logger.warn("Url \"{}\" is not a file!", url.toURI());
                }
            }

            // add interceptor
            if (plugBase != null) {
                List<String> classNames = findClassNames(plugBase);
                for (int i = 0; i < classNames.size(); i++) {
                    String s = classNames.get(i);
                    Object interceptorInstance = Class.forName(s).newInstance();
                    if (interceptorInstance instanceof Interceptor) {
                        logger.info("Add Mybatis-Interceptor \"{}\".", s);
                        configuration.addInterceptor((Interceptor) interceptorInstance);
                    }
                }
            }

            return new SqlSessionFactoryBuilder().build(configuration);
        } catch (Exception e) {
            logger.error("init mybatis factory failed!", e);
            e.printStackTrace(System.err);
            System.exit(-1);
            return null;
        }
    }

    public static List<String> findClassNames(String dir) {
        Enumeration<URL> urlEnumeration = null;
        try {
            urlEnumeration = Thread.currentThread().getContextClassLoader().getResources(dir);
        } catch (IOException e) {
            return Collections.emptyList();
        }
        List<String> fileNames = new ArrayList<>();
        while (urlEnumeration.hasMoreElements()) {
            try {
                URL url = urlEnumeration.nextElement();
                if ("jar".equalsIgnoreCase(url.getProtocol())) {
                    int n = url.getPath().lastIndexOf('!');
                    String jarFilePath = url.getPath().substring(0, n);
                    URL fileUrl = new URL(jarFilePath);
                    if (!new File(fileUrl.toURI()).exists()) {
                        continue;
                    }
                    JarFile jarFile = new JarFile(new File(fileUrl.toURI()));
                    Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
                    while (jarEntryEnumeration.hasMoreElements()) {
                        JarEntry jarEntry = jarEntryEnumeration.nextElement();
                        if (jarEntry.getName().toLowerCase().endsWith(".class")) {
                            fileNames.add(jarEntry.getName().substring(0, jarEntry.getName().indexOf(".")).replace('/', '.'));
                        }
                    }
                } else {
                    getAllFileNames(new File(url.toURI()), dir.lastIndexOf('/') == -1 ? "" : (dir.substring(0, dir.lastIndexOf('/'))).replace('/', '.'), fileNames);
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
        return fileNames;
    }

    public static void getAllFileNames(File dir, String lastFileName, List<String> fileNames) {
        if (dir.isFile()) {
            if (dir.getName().toLowerCase().endsWith(".class")){
                fileNames.add(lastFileName + (lastFileName.length() != 0 ? "." : "") + dir.getName().substring(0, dir.getName().lastIndexOf('.')));
            }
        } else {
            File[] files = dir.listFiles();
            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                getAllFileNames(file, lastFileName + (lastFileName.length() != 0 ? "." : "") + dir.getName(), fileNames);
            }
        }
    }

}
